package asteroids.entity;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

import org.apache.log4j.Logger;

import asteroids.engine.Utils;

public abstract class GameObject
{
    Logger logger = Logger.getLogger(GameObject.class);

    protected int mass;
    protected double x_position;
    protected double y_position;
    protected double x_speed;
    protected double y_speed;
    protected double rotation;
    protected double rot_speed;
    protected double max_radius;
    protected ObjectType objectType;
    protected String affiliation;

    protected double integrity = 1;
    protected boolean invincible; // in case we want to make things not killable

    protected Utils.PolarCoordinate[] objectShape;
    protected Utils.CartesianCoordinate[] oldObjectShape = null;

    protected ArrayList<Icing> icing = null;

    protected Color color = Color.WHITE;
    protected boolean alive = true;
    protected boolean drawMe = true;

    private int score = 0;
    protected int value = 0;

    public enum ObjectType
    {
        SHIP, BULLET, ASTEROID, ICING
    };

    // sticking with Affiliation Strings for now, for INFINITE EXTENSIBILITY
    public final static String NONE = "NONE";
    public final static String PLAYER = "PLAYER";
    public final static String ALIENS = "ALIENS";

    // enum Affiliation
    // {
    // NONE, PLAYER, ALIENS
    // };

    public GameObject(double x, double y, Utils.PolarCoordinate[] shape, ObjectType type,
            String affiliation, int mass)
    {
        this.x_position = x;
        this.y_position = y;
        this.objectShape = shape;
        this.objectType = type;
        this.mass = mass;
        this.affiliation = affiliation;
        invincible = false;

        icing = new ArrayList<Icing>();

        max_radius = 0;
        for( Utils.PolarCoordinate p : shape )
        {
            if( p.getRadius() > max_radius )
            {
                max_radius = p.getRadius();
            }
        }
    }

    public GameObject(double x, double y, Utils.PolarCoordinate[] shape, ObjectType type)
    {
        this(x, y, shape, type, "NONE", 5);
    }

    public void update()
    {
        x_position = x_position + x_speed;
        y_position = y_position + y_speed;
        rotation = rotation + rot_speed;

        // screenwrap
        if( x_position > Utils.GAME_WIDTH )
        {
            x_position = x_position - Utils.GAME_WIDTH;
        }
        if( y_position > Utils.GAME_HEIGHT )
        {
            y_position = y_position - Utils.GAME_HEIGHT;
        }
        if( x_position < 0 )
        {
            x_position = x_position + Utils.GAME_WIDTH;
        }
        if( y_position < 0 )
        {
            y_position = y_position + Utils.GAME_HEIGHT;
        }

        for( int k = icing.size() - 1; k >= 0; k-- )
        {
            Icing i = icing.get(k);
            if( i.isAlive() )
            {
                i.update();
            }
            else
            {
                icing.remove(k);
                i = null;
            }
        }
    }

    public void setColor(Color c)
    {
        color = c;
    }

    public void setVelocity(Utils.CartesianCoordinate c)
    {
        setVelocity(c.getXComponent(), c.getYComponent());
    }

    public void setVelocity(double xspeed, double yspeed)
    {
        x_speed = xspeed;
        y_speed = yspeed;
    }

    public void addVelocity(Utils.CartesianCoordinate c)
    {
        x_speed += c.getXComponent();
        y_speed += c.getYComponent();
    }

    public void addVelocity(Utils.PolarCoordinate p)
    {
        Utils.CartesianCoordinate c = Utils.cartesianFromPolar(p);
        x_speed += c.getXComponent();
        y_speed += c.getYComponent();
    }

    // public void push(Utils.CartesianCoordinate force)
    // {
    // Utils.CartesianCoordinate velocity =
    // Utils.createCartesianCoordinate(x_speed, y_speed);
    // }

    public void setRotation(double r)
    {
        rotation = r;
    }

    public void setRotationSpeed(double rs)
    {
        rot_speed = rs;
    }

    public void setPosition(double x, double y)
    {
        x_position = x;
        y_position = y;
    }

    public double getXPosition()
    {
        return x_position;
    }

    public double getYPosition()
    {
        return y_position;
    }

    public double getRotation()
    {
        return rotation;
    }

    public double getMaxRadius()
    {
        return max_radius;
    }

    public ObjectType getType()
    {
        return objectType;
    }

    public void setAffiliation(String a)
    {
        affiliation = a;
    }

    public String getAffiliation()
    {
        return affiliation;
    }

    public double getIntegrity()
    {
        return integrity;
    }

    public void modifyIntegrityBy(double change)
    {
        integrity += change;
    }

    public Utils.PolarCoordinate[] getObjectShape()
    {
        return Utils.scalePolarShape(objectShape, 1); // return a copy
    }

    public void setObjectShape(Utils.PolarCoordinate[] shape)
    {
        objectShape = shape;
    }

    public Utils.CartesianCoordinate[] getCollisionBounds()
    {
        Utils.CartesianCoordinate[] myPolygon = new Utils.CartesianCoordinate[objectShape.length];
        for( int i = 0; i < objectShape.length; i++ )
        {
            // copy the coordinates, and rotate to reflect the current position
            myPolygon[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation)).shiftBy(
                    x_position, y_position);
        }

        return myPolygon;
    }

    public Utils.CartesianCoordinate[] getOldCollisionBounds()
    {
        return oldObjectShape;
    }

    /**
     * @deprecated called to draw over the object's color in black
     * 
     * @param g
     */
    public void erase(Graphics g)
    {
        if( oldObjectShape != null )
        {
            g.setColor(Color.BLACK);
            Utils.drawCartesianPolygon(oldObjectShape, g);
        }
        for( Icing i : icing )
        {
            i.erase(g);
        }
    }

    /**
     * convert objectShape to Cartesian coordinates, assign it to
     * oldObjectShape, and draw it in this object's color
     * 
     * @param g
     */
    public void draw(Graphics g)
    {
        /*
         * so, only living things can be drawn now. this was done to prevent
         * "Afterimages" of ships and Asteroids after they are destroyed, but
         * before their dependent objects expired. I don't foresee any need to
         * draw things that are not alive, but we'll see.
         */
        if( isAlive() )
        {
            if( oldObjectShape == null ) // instantiate oldObjectShape if needed
            {
                oldObjectShape = new Utils.CartesianCoordinate[objectShape.length];
            }

            for( int i = 0; i < objectShape.length; i++ )
            {
                oldObjectShape[i] = Utils.createCartesianCoordinate(0, 0);
                objectShape[i] = objectShape[i].rotateBy(0);
                // convert the polar coordinates to Cartesian ... and shift to
                // account for the position of the ship
                oldObjectShape[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation))
                        .shiftBy(x_position, y_position);
            }
            if( drawMe )
            {
                // draw the ship in its actual position
                g.setColor(color);
                Utils.drawCartesianPolygon(oldObjectShape, g);

                // Draw around edges
                if( x_position + max_radius > Utils.GAME_WIDTH ) // right edge
                {
                    Utils.CartesianCoordinate[] drawCoords = new Utils.CartesianCoordinate[objectShape.length];
                    for( int i = 0; i < objectShape.length; i++ )
                    {
                        drawCoords[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation))
                                .shiftBy(x_position - Utils.GAME_WIDTH, y_position);
                    }
                    Utils.drawCartesianPolygon(drawCoords, g);
                }
                if( y_position + max_radius > Utils.GAME_HEIGHT ) // bottom edge
                {
                    Utils.CartesianCoordinate[] drawCoords = new Utils.CartesianCoordinate[objectShape.length];
                    for( int i = 0; i < objectShape.length; i++ )
                    {
                        drawCoords[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation))
                                .shiftBy(x_position, y_position - Utils.GAME_HEIGHT);
                    }
                    Utils.drawCartesianPolygon(drawCoords, g);
                }
                if( x_position - max_radius < 0 ) // left edge
                {
                    Utils.CartesianCoordinate[] drawCoords = new Utils.CartesianCoordinate[objectShape.length];
                    for( int i = 0; i < objectShape.length; i++ )
                    {
                        drawCoords[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation))
                                .shiftBy(x_position + Utils.GAME_WIDTH, y_position);
                    }
                    Utils.drawCartesianPolygon(drawCoords, g);
                }
                if( y_position - max_radius < 0 ) // top edge
                {
                    Utils.CartesianCoordinate[] drawCoords = new Utils.CartesianCoordinate[objectShape.length];
                    for( int i = 0; i < objectShape.length; i++ )
                    {
                        drawCoords[i] = Utils.cartesianFromPolar(objectShape[i].rotateBy(rotation))
                                .shiftBy(x_position, y_position + Utils.GAME_HEIGHT);
                    }
                    Utils.drawCartesianPolygon(drawCoords, g);
                }
            }
        }

        for( Icing i : icing )
        {
            if( i.isAlive() )
            {
                i.draw(g);
            }
        }
    }

    public boolean isAlive()
    {
        return alive;
    }

    /**
     * distinct from isAlive, because oftentimes, an object may need to continue
     * existing, controlling, or drawing things despite being dead; isActive
     * should be checked before permanently removing an object, and subclasses
     * should redefine this in light of that purpose.
     * 
     * @return
     */
    public boolean isActive()
    {
        return alive;
    }

    public void destroy()
    {
        alive = false;
        icing.clear();
    }

    public double getCollisionPower(GameObject other)
    {
        return integrity;
    }

    public void damage(double amt)
    {
        integrity = Math.max(integrity - amt, 0);
    }

    /**
     * @param other
     *            - the object with which this is colliding
     * @param power
     *            - the amount of damage other intends to do upon colliding
     */
    public void collide(GameObject other, double power)
    {
        if( other.getType() != ObjectType.ICING && !invincible )
        {
            if( objectType == GameObject.ObjectType.SHIP )
            {
                // logger.warn(this + " was hit!!");
            }
            damage(power);
        }
        if( integrity <= 0 )
        {
            other.modifyScoreBy(value);
            destroy();
        }
    }

    public void modifyScoreBy(int amt)
    {
        score += amt;
    }

    public int getScore()
    {
        return score;
    }
}
